/*
 * Barrett.js
 * Barrett
 * Copyright (C) Jorge H. C. Lucema.
 */

/*global BigInteger */

var Barrett = (function () {

//    "use strict";
    /**
     * 
     * @param {type} m
     * @returns {Barrett_L9.Barrett}
     */
    var Barrett = function (m) {
        this.r2 = new BigInteger();
        this.q3 = new BigInteger();
        BigInteger.ONE.dlShiftTo(2 * m.t, this.r2);
        this.mu = this.r2.divide(m);
        this.m = m;
    };

    Barrett.bConvert = function (x) {
        if (x.s < 0 || x.t > 2 * this.m.t) {
            return x.mod(this.m);
        }
        if (x.compareTo(this.m) < 0) {
            return x;
        }
        var r = new BigInteger();
        x.copyTo(r);
        this.reduce(r);
        return r;
    };

    Barrett.bSqrTo = function (x, r) {
        x.squareTo(r);
        this.reduce(r);
    };

    Barrett.bReduce = function (x) {
        x.drShiftTo(this.m.t - 1, this.r2);
        if (x.t > this.m.t + 1) {
            x.t = this.m.t + 1;
            x.clamp();
        }
        this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3);
        this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2);
        while (x.compareTo(this.r2) < 0) {
            x.dAddOffset(1, this.m.t + 1);
        }
        x.subTo(this.r2, x);
        while (x.compareTo(this.m) >= 0) {
            x.subTo(this.m, x);
        }
    };

    Barrett.bMulTo = function (x, y, r) {
        x.multiplyTo(y, r);
        this.reduce(r);
    };

    Barrett.bRevert = function (x) {
        return x;
    };

    Barrett.prototype.convert = Barrett.bConvert;
    Barrett.prototype.sqrTo = Barrett.bSqrTo;
    Barrett.prototype.reduce = Barrett.bReduce;
    Barrett.prototype.mulTo = Barrett.bMulTo;
    Barrett.prototype.revert = Barrett.bRevert;

    return Barrett;
}());